﻿using JESAI.Caching;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace JESAI.Framework.Idempotents
{
    /// <summary>
    /// 幂等性过滤器
    /// 1. 过滤器类型：Key或Params
    /// 2. Key：通过请求头获取幂等键，并从缓存中获取数据，若存在则返回缓存数据，否则执行后续操作并缓存数据
    /// 3. Params：通过请求参数获取幂等键，并从缓存中获取数据，若存在则返回缓存数据，否则执行后续操作并缓存数据
    /// 
    /// </summary>
    public class IdempotentKeyAttributeFilter : IActionFilter, IResultFilter
    {
        private bool _isIdempotencyCached = false;
        const string IdempotencyKeyHeaderName = "IdempotencyKey"; // 幂等键头名称
        private readonly CacheManager _cacheManager;
        private IdempotentKeyAttributeFilterType _filterType;
        private double _cacheMillisecond { get; set; } = 1000;
        public IdempotentKeyAttributeFilter(CacheManager cacheManager, IdempotentKeyAttributeFilterType filterType, double cacheMillisecond = 1000)
        {
            _cacheManager = cacheManager;
            _filterType = filterType;
            _cacheMillisecond = cacheMillisecond;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {

        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            string idempotencyKey = string.Empty;
            if (_filterType == IdempotentKeyAttributeFilterType.Key)
            {
                if (!context.HttpContext.Request.Headers.TryGetValue(IdempotencyKeyHeaderName, out var idempotencyKeys))
                {
                    context.Result = new BadRequestObjectResult("缺少幂等键");
                    return;
                }
                idempotencyKey = idempotencyKeys.FirstOrDefault();
            }
            else
            {
                idempotencyKey = GetHttpParameterHasCode(context.HttpContext);
            }

            string cacheData = _cacheManager.Get<string>(GetDistributedCacheKey(idempotencyKey));

            if (!string.IsNullOrEmpty(cacheData))
            {
                context.Result = JsonConvert.DeserializeObject<IActionResult>(cacheData);
                _isIdempotencyCached = true;
                return;
            }
        }

        /// <summary>
        /// get http request  parameter code
        /// </summary>
        /// <returns></returns>
        private string GetHttpParameterHasCode(HttpContext httpContext)
        {

            object readFromJson = null;

            try
            {
                if (httpContext.Request.Method != "GET")
                {
                    //readFromJson = await httpContext.Request.ReadFromJsonAsync<object>();

                    httpContext.Request.EnableBuffering();//设置流可以多次读取
                    readFromJson = new StreamReader(httpContext.Request.Body).ReadToEnd();
                    httpContext.Request.Body.Seek(0, SeekOrigin.Begin);//设置流的栈位置回到起点
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            //todo 根据实际项目情况处理，获取Headers toke
            var authorization = httpContext.Request.Headers["Authorization"];

            var queryString = httpContext.Request.QueryString;
            var bodyString = readFromJson == null ? string.Empty : readFromJson.ToString();

            var builder = $"{authorization}-{queryString}-{bodyString}";

            var httpHasCode = builder.GetHashCode();

            return httpHasCode.ToString();
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
            if (_isIdempotencyCached) return;

            var result = context.Result as ObjectResult;
            if (result != null)
            {
                _cacheManager.Set<string>(
                    GetDistributedCacheKey(context.HttpContext.Request.Headers[IdempotencyKeyHeaderName]),
                    JsonConvert.SerializeObject(result), TimeSpan.FromMilliseconds(_cacheMillisecond));
            }
        }



        // 辅助函数生成缓存键名
        private static string GetDistributedCacheKey(string idempotencyKey) => $"Idempotency:{idempotencyKey}";

        public void OnResultExecuting(ResultExecutingContext context)
        {

        }
    }

    public enum IdempotentKeyAttributeFilterType
    {
        Key,
        Params
    }
}
